package com.yilian.reflection;

import java.lang.reflect.*;

/**
 * Java反射功能演示类
 * 
 * 这个类展示了Java反射机制的各种基本用法和技巧，包括：
 * 1. 获取Class对象的多种方式
 * 2. 获取类的结构信息（字段、方法、构造器）
 * 3. 通过反射创建对象实例
 * 4. 通过反射访问和修改字段值
 * 5. 通过反射调用各种方法
 * 6. 获取泛型类型信息
 * 7. 实现动态代理
 * 
 * 学习要点：
 * - 反射是Java语言的一个强大特性，允许程序在运行时检查、修改类的结构和行为
 * - 反射可以绕过访问控制，访问私有成员
 * - 反射在框架开发中非常重要，如Spring、Hibernate等
 * - 反射操作比直接调用慢，在性能敏感的场景要谨慎使用
 * 
 * @author 练习项目
 * @version 1.0
 */
public class ReflectionDemo   {
    
    /**
     * 主方法 - 程序入口点
     * 依次演示反射的各种功能
     * 
     * @param args 命令行参数（未使用）
     */
    public static void main(String[] args) {
        System.out.println("=== Java反射功能演示 ===\n");
        
        // 1. 获取Class对象的三种方式
        demonstrateClassObtaining();
        
        // 2. 获取类的基本信息
        demonstrateClassInfo();
        
        // 3. 获取构造器信息
        demonstrateConstructors();
        
        // 4. 获取字段信息
        demonstrateFields();
        
        // 5. 获取方法信息
        demonstrateMethods();
        
        // 6. 通过反射创建对象
        demonstrateObjectCreation();
        
        // 7. 通过反射访问和修改字段
        demonstrateFieldAccess();
        
        // 8. 通过反射调用方法
        demonstrateMethodInvocation();
        
        // 9. 获取泛型信息
        demonstrateGenericInfo();
        
        // 10. 动态代理示例
        demonstrateDynamicProxy();
    }
    
    /**
     * 演示获取Class对象的三种方式
     * 
     * 在Java中，每个类都有一个Class对象，它包含了该类的所有信息。
     * 获取Class对象是使用反射的第一步，有三种主要方式：
     * 1. 类名.class - 编译时确定，最常用
     * 2. 对象.getClass() - 运行时确定，需要先有对象实例
     * 3. Class.forName() - 通过字符串获取，最灵活但可能抛出异常
     * 
     * 重要概念：
     * - Class对象是单例的，同一个类的Class对象在JVM中只有一个
     * - Class.forName()会触发类的加载和初始化
     * - 这三种方式获取的Class对象是完全相同的
     */
    public static void demonstrateClassObtaining() {
        System.out.println("1. 获取Class对象的三种方式:");
        System.out.println("----------------------------------------");
        
        try {
            // 方式1：通过类名.class
            // 这是最直接的方式，编译时就能确定，性能最好
            Class<?> class1 = Person.class;
            System.out.println("方式1 - Person.class: " + class1.getName());
            
            // 方式2：通过对象.getClass()
            // 这种方式需要先有对象实例，适用于运行时确定类型
            Person person = new Person();
            Class<?> class2 = person.getClass();
            System.out.println("方式2 - person.getClass(): " + class2.getName());
            
            // 方式3：通过Class.forName()
            // 这种方式最灵活，可以通过字符串动态获取类，但可能抛出ClassNotFoundException
            // 常用于配置文件中指定类名，或动态加载类
            Class<?> class3 = Class.forName("com.yilian.reflection.Person");
            System.out.println("方式3 - Class.forName(): " + class3.getName());
            
            // 验证三种方式获取的Class对象是否相同
            // 由于Class对象是单例的，这三种方式获取的应该是同一个对象
            System.out.println("三种方式获取的Class对象是否相同: " + 
                             (class1 == class2 && class2 == class3));
            
        } catch (ClassNotFoundException e) {
            // Class.forName()可能抛出的异常
            System.err.println("类未找到: " + e.getMessage());
            e.printStackTrace();
        }
        System.out.println();
    }
    
    /**
     * 演示获取类的基本信息
     * 
     * Class对象包含了类的所有元数据信息，通过这些信息可以：
     * - 了解类的结构（是否为接口、数组、基本类型等）
     * - 获取类的继承关系（父类、实现的接口）
     * - 分析类的特性（是否为匿名类、局部类等）
     * 
     * 这些信息在框架开发中非常重要，比如：
     * - 依赖注入框架需要知道类的继承关系
     * - ORM框架需要分析类的字段结构
     * - 序列化框架需要了解类的特性
     */
    public static void demonstrateClassInfo() {
        System.out.println("2. 获取类的基本信息:");
        System.out.println("----------------------------------------");
        
        Class<?> personClass = Person.class;
        // 获取类的基本信息
        System.out.println("类名: " + personClass.getName());           // 完整类名（包含包名）
        System.out.println("简单类名: " + personClass.getSimpleName()); // 简单类名（不包含包名）
        System.out.println("包名: " + personClass.getPackage().getName()); // 包名
        
        // 检查类的类型特征
        System.out.println("是否为接口: " + personClass.isInterface());     // 检查是否为接口
        System.out.println("是否为数组: " + personClass.isArray());       // 检查是否为数组
        System.out.println("是否为基本类型: " + personClass.isPrimitive()); // 检查是否为基本类型
        System.out.println("是否为枚举: " + personClass.isEnum());       // 检查是否为枚举
        System.out.println("是否为注解: " + personClass.isAnnotation()); // 检查是否为注解
        
        // 检查类的声明特征
        System.out.println("是否为匿名类: " + personClass.isAnonymousClass()); // 检查是否为匿名类
        System.out.println("是否为局部类: " + personClass.isLocalClass());   // 检查是否为局部类
        System.out.println("是否为成员类: " + personClass.isMemberClass()); // 检查是否为成员类
        
        // 获取继承关系信息
        // 获取父类 - 如果类没有显式继承，则父类为Object
        Class<?> superClass = personClass.getSuperclass();
        System.out.println("父类: " + (superClass != null ? superClass.getName() : "无"));
        
        // 获取实现的接口 - 返回类直接实现的接口，不包括继承的接口
        Class<?>[] interfaces = personClass.getInterfaces();
        System.out.println("实现的接口数量: " + interfaces.length);
        for (Class<?> interfaceClass : interfaces) {
            System.out.println("  - " + interfaceClass.getName());
        }
        System.out.println();
    }
    
    /**
     * 演示获取构造器信息
     * 
     * 构造器是创建对象的关键，通过反射可以：
     * - 获取类的所有构造器信息
     * - 动态选择构造器创建对象
     * - 绕过访问控制访问私有构造器
     * 
     * 重要概念：
     * - getConstructors()只返回公共构造器
     * - getDeclaredConstructors()返回所有声明的构造器（包括私有）
     * - 私有构造器需要setAccessible(true)才能使用
     * - 构造器可以用于依赖注入框架
     */
    public static void demonstrateConstructors() {
        System.out.println("3. 获取构造器信息:");
        System.out.println("----------------------------------------");
        
        Class<?> personClass = Person.class;
        
        // 获取所有公共构造器
        // 只返回public修饰的构造器，包括继承的公共构造器
        Constructor<?>[] publicConstructors = personClass.getConstructors();
        System.out.println("公共构造器数量: " + publicConstructors.length);
        for (Constructor<?> constructor : publicConstructors) {
            System.out.println("  - " + constructor);
        }
        
        // 获取所有构造器（包括私有）
        // 返回类中声明的所有构造器，不包括继承的构造器
        Constructor<?>[] allConstructors = personClass.getDeclaredConstructors();
        System.out.println("所有构造器数量: " + allConstructors.length);
        for (Constructor<?> constructor : allConstructors) {
            System.out.println("  - " + constructor);
        }
        
        // 获取特定构造器
        // 根据参数类型获取特定的构造器，用于精确控制对象创建
        try {
            Constructor<?> constructor = personClass.getConstructor(String.class, int.class);
            System.out.println("获取特定构造器: " + constructor);
        } catch (NoSuchMethodException e) {
            System.err.println("构造器未找到: " + e.getMessage());
            e.printStackTrace();
        }
        System.out.println();
    }
    
    /**
     * 演示获取字段信息
     * 
     * 字段是类的数据成员，通过反射可以：
     * - 获取字段的类型、修饰符等信息
     * - 动态读取和修改字段值
     * - 绕过访问控制访问私有字段
     * 
     * 重要概念：
     * - getFields()返回公共字段（包括继承的）
     * - getDeclaredFields()返回声明的字段（不包括继承的）
     * - 私有字段需要setAccessible(true)才能访问
     * - 字段信息用于ORM、序列化等场景
     */
    public static void demonstrateFields() {
        System.out.println("4. 获取字段信息:");
        System.out.println("----------------------------------------");
        
        Class<?> personClass = Person.class;
        
        // 获取所有公共字段
        // 返回public修饰的字段，包括从父类继承的公共字段
        Field[] publicFields = personClass.getFields();
        System.out.println("公共字段数量: " + publicFields.length);
        for (Field field : publicFields) {
            System.out.println("  - " + field);
        }
        
        // 获取所有字段（包括私有）
        // 返回类中声明的所有字段，不包括继承的字段
        Field[] allFields = personClass.getDeclaredFields();
        System.out.println("所有字段数量: " + allFields.length);
        for (Field field : allFields) {
            System.out.println("  - " + field);
            // 获取字段的修饰符信息
            System.out.println("    修饰符: " + Modifier.toString(field.getModifiers()));
            // 获取字段的类型信息
            System.out.println("    类型: " + field.getType().getName());
        }
        
        // 获取特定字段
        // 根据字段名获取特定的字段，用于精确控制字段访问
        try {
            Field nameField = personClass.getDeclaredField("name");
            System.out.println("获取特定字段: " + nameField);
        } catch (NoSuchFieldException e) {
            System.err.println("字段未找到: " + e.getMessage());
            e.printStackTrace();
        }
        System.out.println();
    }
    
    /**
     * 演示获取方法信息
     * 
     * 方法是类的行为成员，通过反射可以：
     * - 获取方法的签名、修饰符、返回类型等信息
     * - 动态调用方法
     * - 绕过访问控制调用私有方法
     * 
     * 重要概念：
     * - getMethods()返回公共方法（包括继承的）
     * - getDeclaredMethods()返回声明的方法（不包括继承的）
     * - 私有方法需要setAccessible(true)才能调用
     * - 重载方法通过参数类型区分
     */
    public static void demonstrateMethods() {
        System.out.println("5. 获取方法信息:");
        System.out.println("----------------------------------------");
        
        Class<?> personClass = Person.class;
        
        // 获取所有公共方法（包括继承的）
        // 返回public修饰的方法，包括从Object类继承的方法
        Method[] publicMethods = personClass.getMethods();
        System.out.println("公共方法数量: " + publicMethods.length);
        for (Method method : publicMethods) {
            System.out.println("  - " + method);
        }
        
        // 获取所有方法（包括私有，但不包括继承的）
        // 返回类中声明的所有方法，不包括继承的方法
        Method[] allMethods = personClass.getDeclaredMethods();
        System.out.println("所有方法数量: " + allMethods.length);
        for (Method method : allMethods) {
            System.out.println("  - " + method);
            // 获取方法的修饰符信息
            System.out.println("    修饰符: " + Modifier.toString(method.getModifiers()));
            // 获取方法的返回类型
            System.out.println("    返回类型: " + method.getReturnType().getName());
            
            // 获取方法的参数信息
            Class<?>[] paramTypes = method.getParameterTypes();
            System.out.println("    参数类型: " + (paramTypes.length > 0 ? 
                java.util.Arrays.toString(paramTypes) : "无参数"));
        }
        
        // 获取特定方法
        // 根据方法名和参数类型获取特定的方法，用于精确控制方法调用
        try {
            Method setNameMethod = personClass.getMethod("setName", String.class);
            System.out.println("获取特定方法: " + setNameMethod);
        } catch (NoSuchMethodException e) {
            System.err.println("方法未找到: " + e.getMessage());
            e.printStackTrace();
        }
        System.out.println();
    }
    
    /**
     * 演示通过反射创建对象
     * 
     * 反射创建对象是反射的重要应用，可以：
     * - 动态选择构造器创建对象
     * - 绕过访问控制使用私有构造器
     * - 实现工厂模式、依赖注入等设计模式
     * 
     * 重要概念：
     * - newInstance()只能调用无参构造器
     * - Constructor.newInstance()可以调用任意构造器
     * - 私有构造器需要setAccessible(true)
     * - 异常处理很重要，因为构造器可能抛出各种异常
     */
    public static void demonstrateObjectCreation() {
        System.out.println("6. 通过反射创建对象:");
        System.out.println("----------------------------------------");
        
        Class<?> personClass = Person.class;
        
        try {
            // 使用默认构造器创建对象
            // newInstance()只能调用无参构造器，如果类没有无参构造器会抛出异常
            Person person1 = (Person) personClass.newInstance();
            System.out.println("使用默认构造器创建: " + person1);
            
            // 使用带参数构造器创建对象
            // 先获取构造器，再调用newInstance()创建对象
            Constructor<?> constructor = personClass.getConstructor(String.class, int.class);
            Person person2 = (Person) constructor.newInstance("张三", 25);
            System.out.println("使用带参数构造器创建: " + person2);
            
            // 使用私有构造器创建对象
            // 私有构造器需要setAccessible(true)设置可访问
            Constructor<?> privateConstructor = personClass.getDeclaredConstructor(String.class);
            privateConstructor.setAccessible(true); // 设置可访问，绕过访问控制
            Person person3 = (Person) privateConstructor.newInstance("李四");
            System.out.println("使用私有构造器创建: " + person3);
            
        } catch (Exception e) {
            // 反射创建对象可能抛出多种异常，需要统一处理
            System.err.println("创建对象时发生异常: " + e.getMessage());
            e.printStackTrace();
        }
        System.out.println();
    }
    
    /**
     * 演示通过反射访问和修改字段
     * 
     * 反射访问字段是反射的核心功能，可以：
     * - 动态读取字段值
     * - 动态修改字段值
     * - 绕过访问控制访问私有字段
     * 
     * 重要概念：
     * - Field.get()获取字段值
     * - Field.set()设置字段值
     * - 私有字段需要setAccessible(true)
     * - 静态字段访问时对象参数传null
     */
    public static void demonstrateFieldAccess() {
        System.out.println("7. 通过反射访问和修改字段:");
        System.out.println("----------------------------------------");
        
        try {
            Person person = new Person("王五", 30);
            Class<?> personClass = person.getClass();
            
            // 访问公共字段
            // 公共字段可以直接访问，也可以通过反射访问
            Field publicField = personClass.getField("publicField");
            System.out.println("修改前公共字段值: " + publicField.get(person));
            publicField.set(person, "修改后的公共字段");
            System.out.println("修改后公共字段值: " + publicField.get(person));
            
            // 访问私有字段
            // 私有字段需要setAccessible(true)设置可访问
            Field nameField = personClass.getDeclaredField("name");
            nameField.setAccessible(true); // 设置可访问，绕过访问控制
            System.out.println("修改前私有字段值: " + nameField.get(person));
            nameField.set(person, "赵六");
            System.out.println("修改后私有字段值: " + nameField.get(person));
            
            // 访问静态字段
            // 静态字段属于类而不是实例，访问时对象参数传null
            Field staticField = personClass.getDeclaredField("publicField");
            System.out.println("静态字段值: " + staticField.get(null));
            
        } catch (Exception e) {
            System.err.println("访问字段时发生异常: " + e.getMessage());
            e.printStackTrace();
        }
        System.out.println();
    }
    
    /**
     * 演示通过反射调用方法
     * 
     * 反射调用方法是反射的重要应用，可以：
     * - 动态调用任意方法
     * - 绕过访问控制调用私有方法
     * - 实现方法拦截、AOP等功能
     * 
     * 重要概念：
     * - Method.invoke()调用方法
     * - 私有方法需要setAccessible(true)
     * - 静态方法调用时对象参数传null
     * - 重载方法通过参数类型区分
     */
    public static void demonstrateMethodInvocation() {
        System.out.println("8. 通过反射调用方法:");
        System.out.println("----------------------------------------");
        
        try {
            Person person = new Person("钱七", 28);
            Class<?> personClass = person.getClass();
            
            // 调用公共方法
            // 获取方法并调用，invoke的第一个参数是对象实例
            Method getNameMethod = personClass.getMethod("getName");
            String name = (String) getNameMethod.invoke(person);
            System.out.println("调用getName方法结果: " + name);
            
            // 调用带参数方法
            // 带参数的方法调用需要传递参数值
            Method setNameMethod = personClass.getMethod("setName", String.class);
            setNameMethod.invoke(person, "孙八");
            System.out.println("调用setName方法后: " + person.getName());
            
            // 调用私有方法
            // 私有方法需要setAccessible(true)设置可访问
            Method privateMethod = personClass.getDeclaredMethod("getPrivateInfo");
            privateMethod.setAccessible(true);
            String privateInfo = (String) privateMethod.invoke(person);
            System.out.println("调用私有方法结果: " + privateInfo);
            
            // 调用静态方法
            // 静态方法属于类而不是实例，调用时对象参数传null
            Method staticMethod = personClass.getMethod("getStaticInfo");
            String staticInfo = (String) staticMethod.invoke(null);
            System.out.println("调用静态方法结果: " + staticInfo);
            
            // 调用重载方法
            // 重载方法通过参数类型区分，需要指定正确的参数类型
            Method introduceMethod1 = personClass.getMethod("introduce");
            introduceMethod1.invoke(person);
            
            Method introduceMethod2 = personClass.getMethod("introduce", String.class);
            introduceMethod2.invoke(person, "你好");
            
        } catch (Exception e) {
            System.err.println("调用方法时发生异常: " + e.getMessage());
            e.printStackTrace();
        }
        System.out.println();
    }
    
    /**
     * 演示获取泛型信息
     * 
     * 泛型信息在运行时会被擦除，但反射可以：
     * - 获取字段的泛型类型信息
     * - 获取方法的泛型参数信息
     * - 获取构造器的泛型参数信息
     * 
     * 重要概念：
     * - 泛型在运行时被擦除，但类型信息保留在字节码中
     * - getGenericType()返回泛型类型信息
     * - ParameterizedType表示参数化类型
     * - 泛型信息用于框架开发、序列化等场景
     */
    public static void demonstrateGenericInfo() {
        System.out.println("9. 获取泛型信息:");
        System.out.println("----------------------------------------");
        
        try {
            // 获取字段的泛型信息
            Class<?> personClass = Person.class;
            Field[] fields = personClass.getDeclaredFields();
            
            for (Field field : fields) {
                System.out.println("字段: " + field.getName());
                System.out.println("  类型: " + field.getType().getName());
                
                // 获取泛型类型
                // getGenericType()返回泛型类型信息，可能包含类型参数
                if (field.getGenericType() instanceof ParameterizedType) {
                    ParameterizedType paramType = (ParameterizedType) field.getGenericType();
                    System.out.println("  泛型类型: " + paramType);
                    // 获取实际的类型参数
                    for (Type type : paramType.getActualTypeArguments()) {
                        System.out.println("    实际类型参数: " + type);
                    }
                }
            }
            
        } catch (Exception e) {
            System.err.println("获取泛型信息时发生异常: " + e.getMessage());
            e.printStackTrace();
        }
        System.out.println();
    }
    
    /**
     * 演示动态代理
     * 
     * 动态代理是反射的高级应用，可以：
     * - 在运行时创建代理对象
     * - 拦截方法调用
     * - 实现AOP、事务管理等功能
     * 
     * 重要概念：
     * - Proxy.newProxyInstance()创建代理对象
     * - InvocationHandler处理代理方法调用
     * - 代理对象实现指定的接口
     * - 动态代理广泛用于框架开发
     */
    public static void demonstrateDynamicProxy() {
        System.out.println("10. 动态代理示例:");
        System.out.println("----------------------------------------");
        
        try {
            // 创建目标对象
            Person person = new Person("代理测试", 25);
            
            // 创建代理对象
            // 代理对象实现Studyable接口，拦截接口方法的调用
            Object proxy = Proxy.newProxyInstance(
                Person.class.getClassLoader(),           // 类加载器
                new Class<?>[] { Studyable.class },     // 要实现的接口
                new InvocationHandler() {               // 方法调用处理器
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("代理方法调用: " + method.getName());
                        
                        // 根据方法名提供不同的实现
                        if (method.getName().equals("study")) {
                            System.out.println("代理: 正在学习Java反射");
                            return null;
                        } else if (method.getName().equals("takeExam")) {
                            System.out.println("代理: 正在参加Java反射考试");
                            return null;
                        } else if (method.getName().equals("getSubject")) {
                            return "Java反射";
                        }
                        
                        return null;
                    }
                }
            );
            
            // 通过代理调用方法
            // 代理对象实现了Studyable接口，可以调用接口方法
            Studyable studyable = (Studyable) proxy;
            studyable.study();
            studyable.takeExam();
            System.out.println("科目: " + studyable.getSubject());
            
        } catch (Exception e) {
            System.err.println("创建动态代理时发生异常: " + e.getMessage());
            e.printStackTrace();
        }
        System.out.println();
    }
} 